using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Oops.Xna.Framework
{
	public class Heightmap
	{
		private int width;
		private int depth;

		private VertexPositionNormalTexture[] vertices;
		private int[] indices;

		public Heightmap(int width, int depth)
		{
			this.width = width;
			this.depth = depth;

			Random random = new Random((int)DateTime.Now.Millisecond);
			this.vertices = new VertexPositionNormalTexture[width * depth];
			this.indices = new int[(width - 1) * (depth - 1) * 6];

			int index = 0;

			for (int z = 0; z < depth; z++)
			{
				for (int x = 0; x < width; x++)
				{
					float height = (float)random.NextDouble();

					this.vertices[x + z * width].Position = new Vector3(x - (width >> 1), height, z - (depth >> 1));
					this.vertices[x + z * width].TextureCoordinate = new Vector2((float)x / (float)width - 1.0f, (float)z / (float)depth - 1.0f);

					if (x < width - 1 && z < depth - 1)
					{
						this.indices[index++] = x + z * width;
						this.indices[index++] = (x + 1) + z * width;
						this.indices[index++] = x + (z + 1) * width;

						this.indices[index++] = x + (z + 1) * width;
						this.indices[index++] = (x + 1) + z * width; 
						this.indices[index++] = (x + 1) + (z + 1) * width;
					}
				}
			}

            Vector3 edge1, edge2, tangent;
            for(int z = 0; z < depth; z++)
            {
                for(int x = 0; x < width; x++)
                {
                    index = z * width + x;

                    int x1 = MathExtension.Clamp(x - 1, 0, width - 1);
					int x2 = MathExtension.Clamp(x + 1, 0, width - 1);
					int z1 = MathExtension.Clamp(z - 1, 0, depth - 1);
					int z2 = MathExtension.Clamp(z + 1, 0, depth - 1);

                    Vector3 normal0 = this.vertices[z1 * width + x ].Position;
                    Vector3 normal1 = this.vertices[z * width + x1].Position;
                    Vector3 normal2 = this.vertices[z2 * width + x1].Position;
                    Vector3 normal3 = this.vertices[z2 * width + x ].Position;
                    Vector3 normal4 = this.vertices[z * width + x2].Position;
                    Vector3 normal5 = this.vertices[z1 * width + x2].Position;

                    edge1 = normal0 - this.vertices[index].Position;
                    edge2 = normal1 - this.vertices[index].Position;
                    Vector3.Cross(ref edge1, ref edge2, out normal0);

                    edge1 = normal1 - this.vertices[index].Position;
                    edge2 = normal2 - this.vertices[index].Position;
                    Vector3.Cross(ref edge1, ref edge2, out normal1);

                    edge1 = normal2 - this.vertices[index].Position;
                    edge2 = normal3 - this.vertices[index].Position;
                    Vector3.Cross(ref edge1, ref edge2, out normal2);

                    edge1 = normal3 - this.vertices[index].Position;
                    edge2 = normal4 - this.vertices[index].Position;
                    tangent = edge2;
                    Vector3.Cross(ref edge1, ref edge2, out normal3);

                    edge1 = normal4 - this.vertices[index].Position;
                    edge2 = normal5 - this.vertices[index].Position;
                    Vector3.Cross(ref edge1, ref edge2, out normal4);

                    edge1 = normal5 - this.vertices[index].Position;
                    edge2 = normal0 - this.vertices[index].Position;
                    Vector3.Cross(ref edge1, ref edge2, out normal5);

					Vector3 normal = Vector3.Zero;
                    normal += normal0;
                    normal += normal1;
                    normal += normal2;
                    normal += normal3;
                    normal += normal4;
                    normal += normal5;

                    this.vertices[index].Normal = normal;
                    this.vertices[index].Normal.Normalize();

                    //Vector3.Cross(ref tangent, ref normal, out this.vertices[index].Binormal);
                    //this.vertices[index].Binormal.Normalize();

                    //Vector3.Corss(ref normal, ref this.vertices[index].Binormal, out this.vertices[index].Tangent);
                    //this.vertices[index].Tangent.Normalize();
                }
            }
		}

        public VertexPositionNormalTexture[] Vertices
		{
			get { return this.vertices; }
		}

		public int[] Indices
		{
			get { return this.indices; }
		}

		public int Width
		{
			get { return this.width; }
		}

		public int Depth
		{
			get { return this.depth; }
		}
	}
}
